import os
import Tkinter
import Handler

from xml.sax import handler, make_parser
from StyleSheetsReaderImpl import *

class GuiReaderImpl(handler.ContentHandler):
    ## constructor, providing widget factory and widget parent
    #  @param factory  factory with widget information
    #  @param parent   parent
    def __init__(self, factory, parent):
        self.factory         = factory
        self.root            = parent
        self.parent          = [Handler.RootHandler(factory, self.root)]
        self.menues          = []
        self.widgets         = {}
        self.wid             = None
        self.idActive        = False
        self.idName          = ""
        self.stylesheets     = StyleSheetsReaderImpl(factory.controller.stylesheets)
        self.menuBar         = None
        self.images          = []
        self.pathAndFileName = None
        # those variables are NOT tkinter variables.
        self.variables   = {}

    def findElement(self, name):
        try:
            return self.factory.data[name]
        except KeyError:
            return None

    ## checks whether to ignore given XML tag
    #  @param name the name of the tag
    #  @param attr attributes of the tag (key, value) pairs.
    def ignoreElement(self, name, attr):
        if not self.wid == None:
            if attr.has_key("id"):
                if attr["id"] == self.wid:
                    self.idActive = True
                    self.idName   = name

            return not idActive
        return False

    ## updating root (main window)
    #  @param  attr attributes like title and geometry
    #  @return None
    def updateRoot(self, attr):
        if attr.has_key("title"):
            self.root.title(attr["title"])

        if attr.has_key("alpha"):
            # making whole application transparent
            self.root.wm_attributes("-alpha", float(attr["alpha"]))

        if attr.has_key("resizing"):
            symbols = attr["resizing"].split(",")
            # manipulates resizing on x and y direction
            self.root.resizable(int(symbols[0]), int(symbols[1]))

    ## adding a variable which can be re-used in later sections of the XML
    #  @param  attr expected to have 'name' and 'value'!
    #  @return None
    def addVariable(self, attr):
        assert attr.has_key("name")  and \
               attr.has_key("value") and \
               len(attr) == 2
        # given values will be simply overwritten
        self.variables[attr["name"]] = attr["value"]

    ## start of a XML tag.
    #  @param  name the name of the tag
    #  @param  attr attributes of the tag (key, value) pairs.
    #  @return None
    def startElement(self, name, attr):
        attributes = dict(attr)

        if self.ignoreElement(name, attributes):
            return

        # some attributes to adjust the main window
        if name == "root":
            self.updateRoot(attributes)
            return

        # expected to be the short form of a tag
        # resusable value in other sections of the XML
        if name == "variable":
            self.addVariable(attributes)
            return

        for key, value in attributes.items():
            if value.startswith("$"):
                if self.variables.has_key(value[1:]):
                    attributes[key] = self.variables[value[1:]]

        # searching for register Tkinter element
        element = self.findElement(name)
        if not element == None:
            # is it a widget?
            widget = element.createWidget(self.parent[-1].widget)
            if not widget == None:
                # checking for id and class attributes
                wid = None
                if attributes.has_key("id"): wid = attr["id"]
                key = name
                if attributes.has_key("class"): key += "." + attr["class"]

                # apply default styles first
                for style, value in self.stylesheets.getAttributes(key).items():
                    widget[style] = value

                # checking for a handler
                handler = element.createHandler(widget)
                if not handler == None:
                    handler.setParent(self.parent[-1])

                ignore = False

                # is a menu?
                if name == "menu":
                    if self.menuBar == None:
                        self.menuBar = widget
                        self.menues  = [handler]
                        self.root.config(menu = self.menuBar)
                    else:
                        if not handler == None:
                            handler.setParent(self.menues[-1])

                        title = ""
                        if attributes.has_key("title"):
                            title = attr["title"]
                            if title.startswith("#"):
                                title = self.factory.controller.getText(title)

                        if attr["type"] == "cascade":
                            self.menues[-1].addCascade(title, handler, attributes)
                            self.menues.append(handler)
                        elif attr["type"] == "command":
                            self.menues[-1].addCommand(title, handler, attributes)
                            self.menues.append(handler)
                            ignore = True
                        elif attr["type"] == "checkbutton":
                            self.menues[-1].addCheckbutton(title, handler, attributes)
                            self.menues.append(handler)
                            ignore = True
                        elif attr["type"] == "separator":
                            self.menues[-1].addSeparator()
                            self.menues.append(handler)
                            ignore = True

                elif not handler == None:
                    self.parent.append(handler)

                if not ignore:
                    # mapping attributes to widget and
                    # checking for a id (when available)
                    for key, value in attributes.items():
                        if not key in ["id", "class", "type", "value"]:
                            if key == "text":
                                if value.startswith("#"):
                                    value = self.factory.controller.getText(value)
                                else:
                                    value = value.replace("\\n", "\n")

                            if key == "image":
                                value = Tkinter.PhotoImage(file="plugins/" + value)
                                self.factory.controller.images.append(value)

                            #FIXME: widget[key] = value
                            handler.setAttribute(key, value)

                if not handler == None:
                    handler.initialize(wid, attributes)
            else:
                handler = element.createHandler(self.parent[-1].widget)
                if handler:
                    handler.setParent(self.parent[-1])
                    handler.initialize(attributes)

    def endElement(self, name):
        if name in ["root"]:
            return

        if name in ["menu"]:
            if len(self.menues) == 1 and self.menues[0].widget == self.menuBar:
                return
        else:
            if len(self.parent) == 1 and self.parent[0].widget == self.root:
                return

        if not self.id == None:
            if not self.idActive:
                return
            if name == self.idName:
                self.idActive = False

        if name in ["pack", "bind"]:
            return

        if name in ["menu"]:
            self.menues = self.menues[0:-1]
            return

        handler = self.parent[-1]
        if not handler.packed:
            options = {"expand":"yes", "fill":"both", "side":"top"}
            handler.widget.pack(options)

        self.parent = self.parent[0:-1]


    def parse(self, pathAndFileName, id = None):
        if not os.path.isfile(pathAndFileName):
            return None

        styleSheetFileName = pathAndFileName.replace(".xml", "_styles.xml")
        if os.path.isfile(styleSheetFileName):
            self.stylesheets.parse(styleSheetFileName)

        # trying to load a plugin
        plugin = pathAndFileName.replace(".xml", ".py")
        if os.path.isfile(plugin):
            plugin = plugin.replace("/", ".").replace(".py", "")
            model = __import__(plugin, globals(), locals())
            model = eval("model.%s.createModel()" % (plugin.split(".")[1]))
            self.factory.registerModel(model.id(), model)

        self.pathAndFileName = pathAndFileName
        self.id              = id
        parser               = make_parser()

        parser.setContentHandler(self)
        parser.parse(pathAndFileName)

        self.factory.initializeModels()
        return self.root
